package de.fhffm.scs.simulator.regulator;

import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.concurrent.TimeUnit;

import org.csstudio.swt.xygraph.dataprovider.IDataProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.fhffm.scs.simulator.data.BloodSugarDB;
import de.fhffm.scs.simulator.data.BloodSugarLevel;
import de.fhffm.scs.simulator.driver.IndicatorDriver;
import de.fhffm.scs.simulator.driver.InjectorDriver;
import de.fhffm.scs.simulator.driver.SensorDriver;
import de.fhffm.scs.simulator.scheduler.Scheduler;

public class SugarLevelControllerImpl implements SugarLevelController {

    private static Logger LOGGER = LoggerFactory
        .getLogger(SugarLevelControllerImpl.class);

    BloodSugarDB bloodsugarlevel;
    private Scheduler scheduler;
    private BloodSugarEstimator estimator;
    private UnitCalc unitCalc;
    private long timeOfLastInjectionTrigger;
    private InjectorDriver injector;
    private IndicatorDriver indicator;

    public SugarLevelControllerImpl(InjectorDriver injector, IndicatorDriver indicator, Scheduler scheduler,
        UnitCalc unitCalc,
        BloodSugarEstimator estimator) {
        super();
        this.indicator = indicator;
        this.injector = injector;
        this.scheduler = scheduler;
        this.unitCalc = unitCalc;
        this.estimator = estimator;
        bloodsugarlevel = new BloodSugarDB();

        timeOfLastInjectionTrigger = scheduler.getCurrentTimestamp();
    }

    private void triggerInjection(BloodSugarLevel bslToReduce) {
        int u = unitCalc.getUnitsNeededToReduceBloodSugarLevel(bslToReduce);
        injector.injectinsulin(u);
        LOGGER.info("Injected {} insulin unit{}", u, u > 1 ? "s" : "");
        timeOfLastInjectionTrigger = scheduler.getCurrentTimestamp();
    }

    @Override
    public void update(Observable o, Object arg) {
        long halfAnHour = TimeUnit.MINUTES.toMillis(30);

        BloodSugarLevel bsl = (BloodSugarLevel) arg;
        this.bloodsugarlevel.addBloodSugarLevel(bsl);

        LOGGER.debug("Current blood sugar level measured is {}", arg.toString());

        // check wether estimation is needed
        boolean wasInsulinAlreadyTriggered = wasInsulinAlreadyTriggered();
        if (wasInsulinAlreadyTriggered) {
            return;
        }

        if (BloodSugarLevel.HI.contains(bsl)) {
            triggerInjection(bsl);
            indicator.indicateBSLCritical();
        } else {
            indicator.indicateBSLOK();
        }
        return;
        /*
         * // get future bsl
         * BloodSugarLevel lastbsl = (BloodSugarLevel) bloodsugarlevel
         * .getSample(bloodsugarlevel.getSize() - 1);
         * 
         * long timeLastBSLwasRead = lastbsl.getTimestamp();
         * 
         * List<BloodSugarLevel> toEstimateFor = getMeassuredBloodSugarLevelsInTimeRange(
         * timeLastBSLwasRead - halfAnHour, timeLastBSLwasRead);
         * 
         * BloodSugarLevel estimatedFutureBloodSugarLevel = estimator
         * .getEstimatedFutureBloodSugarLevel(toEstimateFor, halfAnHour);
         * 
         * if (BloodSugarLevel.HI.contains(estimatedFutureBloodSugarLevel)) {
         * triggerInjection(estimatedFutureBloodSugarLevel);
         * }
         */
    }

    public boolean wasInsulinAlreadyTriggered() {
        long halfAnHour = TimeUnit.MINUTES.toMillis(15);
        long currentTimestamp = scheduler.getCurrentTimestamp();
        long diff = currentTimestamp - timeOfLastInjectionTrigger;
        boolean b = (diff <= halfAnHour);
        return b;
    }

    List<BloodSugarLevel> getMeassuredBloodSugarLevelsInTimeRange(long begin,
        long end) {
        List<BloodSugarLevel> l = new ArrayList<BloodSugarLevel>();
        for (int i = 0; i < bloodsugarlevel.getSize(); i++) {
            BloodSugarLevel b = (BloodSugarLevel) bloodsugarlevel.getSample(i);
            if (b.getTimestamp() >= begin && b.getTimestamp() <= end) {
                l.add(b);
            }
        }
        return l;
    }

    public BloodSugarDB getAllMeassureBloodSugarLevel() {
        return bloodsugarlevel;
    }

    @Override
    public IDataProvider getBloodSugarDB() {
        return bloodsugarlevel;
    }

    @Override
    public Scheduler getScheduler() {
        return scheduler;
    }

    @Override
    public void registerIndicationDriver(IndicatorDriver indicatorDriver) {
        this.indicator = indicatorDriver;

    }

}
